// HwndUtil.java
package org.zjx.util;

import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.ptr.IntByReference;
import lombok.extern.slf4j.Slf4j;

import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

/**
 * 窗口句柄工具类 - 改进错误处理和性能
 */
@Slf4j
public class HwndUtil {
    private static final int MAX_CLASS_NAME_LENGTH = 256;
    private static final int MAX_TITLE_LENGTH = 1024;
    private static final int WM_CLOSE = 0x0010;

    // 私有构造函数防止实例化
    private HwndUtil() {}

    public static String getWindowClassName(WinDef.HWND hwnd) {
        if (hwnd == null) {
            return null;
        }

        try {
            char[] buffer = new char[MAX_CLASS_NAME_LENGTH];
            int length = User32.INSTANCE.GetClassName(hwnd, buffer, buffer.length);
            return length > 0 ? new String(buffer, 0, length) : null;
        } catch (Exception e) {
            log.error("获取窗口类名失败: {}", hwnd, e);
            return null;
        }
    }

    public static String getWindowTitle(WinDef.HWND hwnd) {
        if (hwnd == null) {
            return null;
        }

        try {
            char[] buffer = new char[MAX_TITLE_LENGTH];
            int length = User32.INSTANCE.GetWindowText(hwnd, buffer, buffer.length);
            return length > 0 ? new String(buffer, 0, length) : null;
        } catch (Exception e) {
            log.error("获取窗口标题失败: {}", hwnd, e);
            return null;
        }
    }

    public static int getWindowProcessId(WinDef.HWND hwnd) {
        if (hwnd == null) {
            return 0;
        }

        try {
            IntByReference processId = new IntByReference();
            User32.INSTANCE.GetWindowThreadProcessId(hwnd, processId);
            return processId.getValue();
        } catch (Exception e) {
            log.error("获取窗口进程ID失败: {}", hwnd, e);
            return 0;
        }
    }

    public static boolean haveWindow(WinDef.HWND hwnd) {
        if (hwnd == null) {
            return false;
        }

        try {
            return User32.INSTANCE.IsWindow(hwnd);
        } catch (Exception e) {
            log.error("检查窗口是否存在失败: {}", hwnd, e);
            return false;
        }
    }

    public static List<WinDef.HWND> getAllTopWindows() {
        List<WinDef.HWND> windows = new ArrayList<>();
        try {
            User32.INSTANCE.EnumWindows((hwnd, data) -> {
                windows.add(hwnd);
                return true;
            }, null);
        } catch (Exception e) {
            log.error("枚举顶层窗口失败", e);
        }
        return windows;
    }

    public static List<WinDef.HWND> getVisibleTopWindows() {
        List<WinDef.HWND> windows = new ArrayList<>();
        try {
            User32.INSTANCE.EnumWindows((hwnd, data) -> {
                if (isWindowVisible(hwnd)) {
                    windows.add(hwnd);
                }
                return true;
            }, null);
        } catch (Exception e) {
            log.error("枚举可见顶层窗口失败", e);
        }
        return windows;
    }

    public static String getWindowInfo(WinDef.HWND hwnd) {
        if (hwnd == null) {
            return "null";
        }

        try {
            String className = getWindowClassName(hwnd);
            String title = getWindowTitle(hwnd);
            int pid = getWindowProcessId(hwnd);
            Rectangle bounds = getWindowRectangle(hwnd);

            return String.format("句柄: 0x%X, 类名: %s, 标题: %s, 进程ID: %d, 位置: [%d,%d,%d,%d]",
                    hwnd.hashCode(), className, title, pid,
                    bounds.x, bounds.y, bounds.width, bounds.height);
        } catch (Exception e) {
            return String.format("句柄: 0x%X (获取信息失败: %s)", hwnd.hashCode(), e.getMessage());
        }
    }

    public static List<WinDef.HWND> findWindows(String className, String titleName) {
        return findWindows(className, titleName, null);
    }

    public static List<WinDef.HWND> findWindows(String className, String titleName,
                                                Predicate<WinDef.HWND> filter) {
        List<WinDef.HWND> results = new ArrayList<>();
        WinDef.HWND previous = null;

        try {
            for (int i = 0; i < 100; i++) { // 限制最大查找数量
                WinDef.HWND hwnd = User32.INSTANCE.FindWindowEx(null, previous, className, titleName);
                if (hwnd == null) {
                    break;
                }

                if (filter == null || filter.test(hwnd)) {
                    results.add(hwnd);
                }

                previous = hwnd;
            }
        } catch (Exception e) {
            log.error("查找窗口失败: className={}, title={}", className, titleName, e);
        }

        return results;
    }

    public static boolean isWindowVisible(WinDef.HWND hwnd) {
        if (hwnd == null) {
            return false;
        }

        try {
            return User32.INSTANCE.IsWindowVisible(hwnd);
        } catch (Exception e) {
            log.error("检查窗口可见性失败: {}", hwnd, e);
            return false;
        }
    }

    public static boolean isWindowEnabled(WinDef.HWND hwnd) {
        if (hwnd == null) {
            return false;
        }

        try {
            return User32.INSTANCE.IsWindowEnabled(hwnd);
        } catch (Exception e) {
            log.error("检查窗口是否启用失败: {}", hwnd, e);
            return false;
        }
    }

    public static WinDef.HWND findWindow(String className, String titleName) {
        List<WinDef.HWND> results = findWindows(className, titleName);
        return results.isEmpty() ? null : results.get(0);
    }

    public static Rectangle getWindowRectangle(WinDef.HWND hwnd) {
        if (hwnd == null) {
            throw new IllegalArgumentException("窗口句柄不能为空");
        }

        try {
            WinDef.RECT rect = new WinDef.RECT();
            if (!User32.INSTANCE.GetWindowRect(hwnd, rect)) {
                throw new RuntimeException("获取窗口矩形失败");
            }
            return new Rectangle(rect.left, rect.top,
                    rect.right - rect.left, rect.bottom - rect.top);
        } catch (Exception e) {
            log.error("获取窗口矩形失败: {}", hwnd, e);
            throw new RuntimeException("获取窗口矩形失败: " + e.getMessage(), e);
        }
    }

    public static void preposition(WinDef.HWND hwnd) {
        if (hwnd == null) {
            throw new IllegalArgumentException("窗口句柄不能为空");
        }

        try {
            // 先尝试SetForegroundWindow
            if (!User32.INSTANCE.SetForegroundWindow(hwnd)) {
                log.warn("SetForegroundWindow 失败，尝试备用方法");
            }
        } catch (Exception e) {
            log.error("前置窗口失败: {}", hwnd, e);
            throw new RuntimeException("前置窗口失败: " + e.getMessage(), e);
        }
    }

    public static void moveWindow(WinDef.HWND hwnd, int x, int y) {
        if (hwnd == null) {
            throw new IllegalArgumentException("窗口句柄不能为空");
        }

        try {
            Rectangle rect = getWindowRectangle(hwnd);
            if (!User32.INSTANCE.MoveWindow(hwnd, x, y, rect.width, rect.height, true)) {
                throw new RuntimeException("移动窗口失败");
            }
        } catch (Exception e) {
            log.error("移动窗口失败: {} to [{}, {}]", hwnd, x, y, e);
            throw new RuntimeException("移动窗口失败: " + e.getMessage(), e);
        }
    }

    public static void resizeWindow(WinDef.HWND hwnd, int width, int height) {
        if (hwnd == null) {
            throw new IllegalArgumentException("窗口句柄不能为空");
        }

        try {
            Rectangle rect = getWindowRectangle(hwnd);
            if (!User32.INSTANCE.MoveWindow(hwnd, rect.x, rect.y, width, height, true)) {
                throw new RuntimeException("调整窗口大小失败");
            }
        } catch (Exception e) {
            log.error("调整窗口大小失败: {} to [{}x{}]", hwnd, width, height, e);
            throw new RuntimeException("调整窗口大小失败: " + e.getMessage(), e);
        }
    }

    public static boolean closeWindow(WinDef.HWND hwnd) {
        return closeWindow(hwnd, 5000);
    }

    public static boolean closeWindow(WinDef.HWND hwnd, long timeoutMs) {
        if (hwnd == null) {
            return false;
        }

        try {
            log.debug("尝试关闭窗口: {}", getWindowInfo(hwnd));

            // 发送关闭消息
            User32.INSTANCE.SendMessage(hwnd, WM_CLOSE, null, null);

            // 等待窗口关闭
            long endTime = System.currentTimeMillis() + timeoutMs;
            while (System.currentTimeMillis() < endTime) {
                if (!User32.INSTANCE.IsWindow(hwnd)) {
                    log.info("窗口关闭成功: {}", getWindowInfo(hwnd));
                    return true;
                }
                TimeUnit.MILLISECONDS.sleep(100);
            }

            log.warn("窗口关闭超时: {}", getWindowInfo(hwnd));
            return false;

        } catch (Exception e) {
            log.error("关闭窗口失败: {}", hwnd, e);
            return false;
        }
    }

    /**
     * 获取窗口的父窗口
     */
    public static WinDef.HWND getParentWindow(WinDef.HWND hwnd) {
        if (hwnd == null) {
            return null;
        }

        try {
            return User32.INSTANCE.GetParent(hwnd);
        } catch (Exception e) {
            log.error("获取父窗口失败: {}", hwnd, e);
            return null;
        }
    }

    /**
     * 获取窗口样式
     */
    public static int getWindowStyle(WinDef.HWND hwnd) {
        if (hwnd == null) {
            return 0;
        }

        try {
            return User32.INSTANCE.GetWindowLong(hwnd, WinUser.GWL_STYLE);
        } catch (Exception e) {
            log.error("获取窗口样式失败: {}", hwnd, e);
            return 0;
        }
    }

    /**
     * 设置窗口样式
     */
    public static boolean setWindowStyle(WinDef.HWND hwnd, int style) {
        if (hwnd == null) {
            return false;
        }

        try {
            User32.INSTANCE.SetWindowLong(hwnd, WinUser.GWL_STYLE, style);
            return true;
        } catch (Exception e) {
            log.error("设置窗口样式失败: {}", hwnd, e);
            return false;
        }
    }
}